/******************************
 * @Developer: many
 * @File: ntpdate.go
 * @Time: 2022/4/28 9:17
******************************/

package lib

import (
	"encoding/binary"
	"flag"
	"net"
	"time"
)

const ntpEpochOffset = 2208988800

type packet struct {
	Settings       uint8
	Stratum        uint8
	Poll           int8
	Precision      int8
	RootDelay      uint32
	RootDispersion uint32
	ReferenceID    uint32
	RefTimeSec     uint32
	RefTimeFrac    uint32
	OrigTimeSec    uint32
	OrigTimeFrac   uint32
	RxTimeSec      uint32
	RxTimeFrac     uint32
	TxTimeSec      uint32
	TxTimeFrac     uint32
}

var (
	ntpaddr string
)

func init() {
	flag.StringVar(&ntpaddr, "ntp", "cn.ntp.org.cn:123", "NTP addr")
}

func NtpDate() (t *time.Time, err error) {
	var conn net.Conn
	defer func() {
		if conn == nil {
			return
		}

		err = conn.Close()
	}()
	if conn, err = net.Dial("udp", ntpaddr); err != nil {
		return nil, err
	}

	if err = conn.SetDeadline(time.Now().Add(5 * time.Second)); err != nil {
		return nil, err
	}

	req := &packet{Settings: 0x1B}

	if err = binary.Write(conn, binary.BigEndian, req); err != nil {
		return nil, err
	}

	rsp := &packet{}
	if err = binary.Read(conn, binary.BigEndian, rsp); err != nil {
		return nil, err
	}

	secs := float64(rsp.TxTimeSec) - ntpEpochOffset
	nanos := (int64(rsp.TxTimeFrac) * 1e9) >> 32
	showtime := time.Unix(int64(secs), nanos)

	return &showtime, nil
}
